//----------------------------------------------------------------------------------------------
//	Filename:	WhittedIntegrator.cpp
//	Author:		Keith Bugeja
//	Date:		27/02/2010
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
#include <iostream>
#include "Integrator/DepthIntegrator.h"

#include "Geometry/Ray.h"
#include "Geometry/Intersection.h"
#include "Sampler/JitterSampler.h"
#include "Material/Material.h"
#include "Material/Matte.h"
#include "Spectrum/Spectrum.h"
#include "Scene/Visibility.h"
#include "Scene/Scene.h"

#include "System/EngineKernel.h"

using namespace Illumina::Core;
//----------------------------------------------------------------------------------------------
DepthIntegrator::DepthIntegrator(const std::string &p_strName)
	: IIntegrator(p_strName) 	
{ }
//----------------------------------------------------------------------------------------------
DepthIntegrator::DepthIntegrator(void)	
{ }
//----------------------------------------------------------------------------------------------
bool DepthIntegrator::Initialise(Scene *p_pScene, ICamera *p_pCamera)
{
	//std::cout << "Depth Tracing Integrator :: Initialise()" << std::endl;
	return true;
}
//----------------------------------------------------------------------------------------------
bool DepthIntegrator::Shutdown(void)
{
	return true;
}
//----------------------------------------------------------------------------------------------
Spectrum DepthIntegrator::Radiance(IntegratorContext *p_pContext, Scene *p_pScene, Intersection &p_intersection, RadianceContext *p_pRadianceContext)
{	
	//----------------------------------------------------------------------------------------------
	// Avoid having to perform multiple checks for a NULL radiance context
	//----------------------------------------------------------------------------------------------
	RadianceContext radianceContext;

	if (p_pRadianceContext == NULL)
		p_pRadianceContext = &radianceContext;
	
	// Initialise context
	p_pRadianceContext->Indirect = 
		p_pRadianceContext->Direct = 
		p_pRadianceContext->Albedo = 1.f;

	// Construct ray from intersection details
	//Ray ray(p_intersection.Surface.RayOriginWS, 
//		p_intersection.Surface.RayDirectionWS); 

	//----------------------------------------------------------------------------------------------
	// If intersection is invalid, return
	//----------------------------------------------------------------------------------------------
	if (!p_intersection.IsValid())
	{
		return p_pRadianceContext->Direct;
	}

	if (p_intersection.GetMaterial() == NULL) { return p_pRadianceContext->Direct; }

	if (!(p_intersection.GetMaterial()->HasBxDFType(BxDF::Type::Transmission, false)))
	{
		Vector3 lightVector(0, -1, 0);
		lightVector.Normalize();

		//----------------------------------------------------------------------------------------------
		// Set spatial information to radiance context
		//----------------------------------------------------------------------------------------------
		p_pRadianceContext->SetSpatialContext(&p_intersection);
		//static std::string na;		

		//if (na!=p_intersection.GetMaterial()->GetName()) 
		//{
		//	std::cout << p_intersection.GetMaterial()->GetName() << std::endl;
		//	na = p_intersection.GetMaterial()->GetName();
		//}

		if (p_intersection.GetMaterial()->GetName() == "LitColour1")
		{
			Vector3 normal = p_intersection.Surface.GeometryBasisWS.W;
			if (normal.Dot(p_intersection.Surface.RayDirectionWS) < 0) normal = -normal;
			float cosTheta = Vector3::Dot(lightVector, normal);
			p_pRadianceContext->Direct = 
				p_intersection.GetMaterial()->F(p_intersection.Surface, p_intersection.Surface.GeometryNormal, p_intersection.Surface.GeometryNormal) * Maths::Max(0.3f, cosTheta);
			p_pRadianceContext->Final = p_pRadianceContext->Direct;
			//	p_intersection.GetMaterial()->F(p_intersection.Surface, p_intersection.Surface.GeometryNormal, p_intersection.Surface.GeometryNormal) * Maths::Max(0.3f, Vector3::Dot(p_intersection.Surface.GeometryBasisWS.W, lightVector));

			//p_pRadianceContext->Final = p_pRadianceContext->Direct =
			//	p_intersection.GetMaterial()->F(p_intersection.Surface, p_intersection.Surface.GeometryNormal, p_intersection.Surface.GeometryNormal) * Maths::Max(0.3f, Vector3::Dot(p_intersection.Surface.GeometryBasisWS.W, lightVector));
		}
		else
		{		
			p_pRadianceContext->Final = p_pRadianceContext->Direct =
			//p_intersection.Surface.Distance;
			p_intersection.GetMaterial()->F(p_intersection.Surface, p_intersection.Surface.GeometryNormal, p_intersection.Surface.GeometryNormal);
			//((MatteMaterial*)p_intersection.GetMaterial())->SampleTexture(p_intersection.Surface, 0);
		}
	}
	else 
	{
		Intersection intersection;
		RadianceContext context;
		Ray ray(p_pRadianceContext->ViewRay);
		ray.Origin = p_intersection.Surface.PointWS + p_intersection.Surface.ShadingNormal.W * 0.0001f;
		p_pRadianceContext->Direct = Radiance(p_pContext, p_pScene, ray, intersection, &context) * 0.5f + Spectrum(0.1f);
	}

	return p_pRadianceContext->Direct;
}
//----------------------------------------------------------------------------------------------
Spectrum DepthIntegrator::Radiance(IntegratorContext *p_pContext, Scene *p_pScene, const Ray &p_ray, Intersection &p_intersection, RadianceContext *p_pRadianceContext)
{
	// Compute intersection step
	if (!p_pScene->Intersects(Ray(p_ray), p_intersection))
	{
		p_intersection.Surface.RayOriginWS = p_ray.Origin;
		p_intersection.Surface.RayDirectionWS = p_ray.Direction;
	}
	return Radiance(p_pContext, p_pScene, p_intersection, p_pRadianceContext);
}
//----------------------------------------------------------------------------------------------